from enum import IntEnum
from dataclasses import dataclass, field
import json

from dataclass_wizard import JSONSerializable


class Brand(IntEnum):
    uncaged = 1
    instinct = 2
    silk = 4
    call_of_the_wild = 3
    silk_showcase = 5


@dataclass
class Link(JSONSerializable):
    """Class represents Link object

    Attributes:
        url (str): The url this link leads to.
        platform (str): The platform this link leads to.
    """

    url: str
    platform: str

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "Url": "url",
            "Platform": "platform",
        }

    @classmethod
    def from_json(cls, data: str) -> "Link":
        d = json.loads(data, strict=False)
        return Link.from_dict(d)


@dataclass
class ReleaseArtist(JSONSerializable):
    """Class represents Release Artist object

    Attributes:
        artist_uuid (str): Artist's unique id.
        position (int): Artist's order in release credit.
        name (str): Artist's name.
        profile_file_uuid (str): ID of the profile image asset:
        platform (str): Artist platform.
        is_public (bool): Whether the artist's profile page is public.
        release_uuid (str): ID of the release this artist is linked to.
        role (str): Artist's role in the release.
        uri (str): Public url slug to the artist's profile page.
    """

    artist_uuid: str
    position: int
    name: str
    profile_file_uuid: str
    platform: str
    is_public: bool
    release_uuid: str
    role: str
    uri: str

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "ArtistId": "artist_uuid",
            "ArtistNumber": "position",
            "Name": "name",
            "ProfileFileId": "profile_file_uuid",
            "Platform": "platform",
            "Public": "is_public",
            "ReleaseId": "release_uuid",
            "Role": "role",
            "URI": "uri",
        }

    @classmethod
    def from_json(cls, data: str) -> "ReleaseArtist":
        d = json.loads(data, strict=False)
        return ReleaseArtist.from_dict(d)


@dataclass
class Release(JSONSerializable):
    """Class represents Release object

    Attributes:
        artists_string (str):
        catalog_id (str): The catalog ID of the release. After Dec 1, 2022 the UPC is used.
        description (str): Description of the release.
        uuid (str): The unique ID of the release.
        release_date (str): ISO8601 timestamp. The release date.
        release_date_timezone (str): Timezone identifier for the release date.
        title (str): The title of the release.
        type (str): The type of the release (Single, EP, Album, etc).
        version (str): The version of the release (shown in parentheses).
        notes (str): Liner Notes for the release.
        artists (list[ReleaseArtist]): List of Artists for the release.
        brand (Brand): The ID of the brand the release belongs to.
        brand_string (str): Human readable name of the brand the release belongs to.
        copyright_p_line (str): Notice of sound recording copyright (℗).
        is_downloadable (bool): Whether the release can be downloaded with Gold.
        featured_artists_string (str): Human readable featured artists (unused?).
        global_release_id (str): The Global Release Identifier of the release.
        genre (str): Primary genre of the release.
        subgenre (str): The subgenre of the release.
        is_early_access (bool): Whether the release is currently in Gold Early Access.
        links (list[Link]): list of Links on this release.
        prerelease_date (str): The pre-release date.
        presave_date (str): The date when the release went up for presave.
        spotify_uuid (str): The spotify id for the release (see Links instead).
        is_streamable (bool): Whether the release can be streamed on the site/player.
        tags (list[str]): The list of tags of the release.
        universal_product_code (str): The Universal Product Code code of the release.
        youtube_url (str): The youtube video for the release (see Links instead).
    """

    artists_string: str
    catalog_id: str
    description: str
    uuid: str
    release_date: str
    release_date_timezone: str
    title: str
    type: str
    version: str
    notes: str | None = None
    artists: list[ReleaseArtist] | None = None
    brand: Brand | None = None
    brand_string: str | None = None
    copyright_p_line: str | None = None
    is_downloadable: bool | None = None
    featured_artists_string: str | None = None
    global_release_id: str | None = None
    genre: str | None = None
    subgenre: str | None = None
    is_early_access: bool | None = None
    links: list[Link] | None = None
    prerelease_date: str | None = None
    presave_date: str | None = None
    spotify_uuid: str | None = None
    is_streamable: bool | None = None
    tags: list[str] | None = None
    universal_product_code: str | None = None
    youtube_url: str | None = None

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "ArtistsTitle": "artists_string",
            "CatalogId": "catalog_id",
            "Description": "description",
            "Id": "uuid",
            "ReleaseDate": "release_date",
            "ReleaseDateTimezone": "release_date_timezone",
            "Title": "title",
            "Type": "type",
            "Version": "version",
            "AlbumNotes": "notes",
            "Artists": "artists",
            "BrandId": "brand",
            "BrandTitle": "brand_string",
            "CopyrightPLine": "copyright_p_line",
            "Downloadable": "is_downloadable",
            "FeaturedArtistsTitle": "featured_artists_string",
            "GRid": "global_release_id",
            "GenrePrimary": "genre",
            "GenreSecondary": "subgenre",
            "InEarlyAccess": "is_early_access",
            "Links": "links",
            "PrereleaseDate": "prerelease_date",
            "PresaveDate": "presave_date",
            "SpotifyId": "spotify_uuid",
            "Streamable": "is_streamable",
            "Tags": "tags",
            "UPC": "universal_product_code",
            "YoutubeUrl": "youtube_url",
        }

    @classmethod
    def from_json(cls, data: str) -> "Release":
        d = json.loads(data, strict=False)
        return Release.from_dict(d)


@dataclass
class TrackArtist(JSONSerializable):
    """Class represents Track Artist object

    Attributes:
        track_uuid (str): ID of the track this artist is linked to.
        uuid (str): Artist's unique id.
        name (str): Artist's name.
        profile_file_uuid (str): ID of the profile image asset.
        is_public (bool): Whether the artist's profile page is public.
        role (str): Artist's role in the release.
        uri (str): Public url slug to the artist's profile page.
    """

    track_uuid: str
    uuid: str
    name: str
    profile_file_uuid: str
    is_public: bool
    role: str
    uri: str

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "CatalogRecordId": "track_uuid",
            "Id": "uuid",
            "Name": "name",
            "ProfileFileId": "profile_file_uuid",
            "Public": "is_public",
            "Role": "role",
            "URI": "uri",
        }

    @classmethod
    def from_json(cls, data: str) -> "TrackArtist":
        d = json.loads(data, strict=False)
        return TrackArtist.from_dict(d)


@dataclass
class Track(JSONSerializable):
    """Class represents Track object

    Attributes:
        artists_string (str): Human readable artists string.
        bpm (int): The track's BPM.
        brand (Brand): The ID of the brand the release belongs to.
        brand_string (str): Human readable name of the brand the release belongs to.
        is_creator_friendly (bool): Whether the track is available for Content Creator licenses.
        debut_date (str): ISO8601 timestamp. The first release date of the track.
        is_downloadable (bool): Whether the track can be downloaded with Gold.
        duration (int): The duration of the track in seconds.
        is_explicit (bool): Whether the track is explicit.
        genre (str): Primary genre of the release.
        subgenre (str): The subgenre of the release.
        isrc (str): The International Standard Recording Code of the track.
        uuid (str): The unique ID of the track.
        is_early_access (bool): Whether the track is currently in Gold Early Access.
        release (Release): The release the track belongs to.
        is_streamable (bool): Whether the track can be streamed on the site/player.
        title (str): The title of the track.
        number (int): The track number of the track in a release.
        version (str): The version of the track (shown in parentheses).
        artists (list[TrackArtist]): The list of artists that produced the track.
        playlist_position (int): The index of the track in a playlist.
        tags (list[str]): The list of tags of the release.
    """

    artists_string: str
    bpm: int
    brand_string: str
    brand: Brand
    is_creator_friendly: bool
    debut_date: str
    is_downloadable: bool
    duration: int
    is_explicit: bool
    genre: str
    subgenre: str
    isrc: str
    uuid: str
    is_early_access: bool
    release: Release
    is_streamable: bool
    title: str
    number: int
    version: str
    artists: list[TrackArtist] | None = None
    playlist_position: int | None = None
    tags: list[str] | None = None

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "ArtistsTitle": "artists_string",
            "BPM": "bpm",
            "Brand": "brand_string",
            "BrandId": "brand",
            "CreatorFriendly": "is_creator_friendly",
            "DebutDate": "debut_date",
            "Downloadable": "is_downloadable",
            "Duration": "duration",
            "Explicit": "is_explicit",
            "GenrePrimary": "genre",
            "GenreSecondary": "subgenre",
            "ISRC": "isrc",
            "Id": "uuid",
            "InEarlyAccess": "is_early_access",
            "Release": "release",
            "Streamable": "is_streamable",
            "Title": "title",
            "TrackNumber": "number",
            "Version": "version",
            "Artists": "artists",
            "PlaylistSort": "playlist_position",
            "Tags": "tags",
        }

    @classmethod
    def from_json(cls, data: str) -> "Track":
        d = json.loads(data, strict=False)
        return Track.from_dict(d)


@dataclass
class ArtistDetails(JSONSerializable):
    """Class represents Details object for Artist object

    Attributes:
        about (str): The artist's bio.
        bookings(str): The artist's bio.
        management (str): Details about artist's management.
    """

    about: str
    bookings: str
    management: str

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "About": "about",
            "Bookings": "bookings",
            "ManagementDetails": "management",
        }

    @classmethod
    def from_json(cls, data: str) -> "ArtistDetails":
        d = json.loads(data, strict=False)
        return ArtistDetails.from_dict(d)


@dataclass
class Artist(JSONSerializable):
    """Class represents Artist object

    Attributes:
        details (ArtistDetails): Artist's bio, bookings and mgmt details.
        name (str): Artist's name.
        is_public (bool): Whether the artist's profile page is public.
        do_show_event (bool): Whether events should be shown.
        uri (str): Public url slug to the artist's profile page.
        active_years (list[int]): Years when the artist has released on Monstercat.
        featured_release_cover_file_uuid (str): File ID for the cover of the featured release.
        featured_release_uuid (str): ID of artist's featured release.
        featured_video_url (str): URL to artist's featured video.
        uuid (str): URL to artist's featured video.
        landscape_file_uuid (str): File ID for landscape.
        links (list[Link]): List of Links to the artist's socials.
        logo_file_uuid (str): File ID for logo.
        portrait_file_uuid (str): File ID for portrait.
        profile_file_uuid (str): profile_file_uuid.
        square_file_uuid (str): File ID for square.
        tags (list[str]): The list of tags for the artist.
    """

    details: ArtistDetails
    name: str
    is_public: bool
    do_show_event: bool
    uri: str
    active_years: list[int] = field(default_factory=list)
    featured_release_cover_file_uuid: str | None = None
    featured_release_uuid: str | None = None
    featured_video_url: str | None = None
    uuid: str | None = None
    landscape_file_uuid: str | None = None
    links: list[Link] = field(default_factory=list)
    logo_file_uuid: str | None = None
    portrait_file_uuid: str | None = None
    profile_file_uuid: str | None = None
    square_file_uuid: str | None = None
    tags: list[str] = field(default_factory=list)

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "Details": "details",
            "Name": "name",
            "Public": "is_public",
            "ShowEvent": "do_show_event",
            "URI": "uri",
            "ActiveYears": "active_years",
            "FeaturedReleaseCoverFileId": "featured_release_cover_file_uuid",
            "FeaturedReleaseId": "featured_release_uuid",
            "FeaturedVideoURL": "featured_video_url",
            "Id": "uuid",
            "LandscapeFileId": "landscape_file_uuid",
            "Links": "links",
            "LogoFileId": "logo_file_uuid",
            "PortraitFileId": "portrait_file_uuid",
            "ProfileFileId": "profile_file_uuid",
            "SquareFileId": "square_file_uuid",
            "Tags": "tags",
        }

    @classmethod
    def from_json(cls, data: str) -> "Artist":
        d = json.loads(data, strict=False)
        return Artist.from_dict(d)


@dataclass
class ReleaseResponse(JSONSerializable):
    """Class represents Release Response object

    Attributes:
        release (Release): Release object.
        tracks (list[Track]): List of Track objects in this release.
    """

    release: Release
    tracks: list[Track]

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            # '__all__': 'True',
            "Release": "release",
            "Tracks": "tracks",
        }

    @classmethod
    def from_json(cls, data: str) -> "ReleaseResponse":
        d = json.loads(data, strict=False)
        return ReleaseResponse.from_dict(d)


@dataclass
class LatestRelease(JSONSerializable):
    """Class represents Latest Release object

    Attributes:
        artists (list[TrackArtist]): The list of artists that produced the track.
        artists_string (str): Human readable artists string.
        bpm (int): The track's BPM.
        brand (Brand): The ID of the brand the release belongs to.
        brand_string (str): Human readable name of the brand the release belongs to.
        is_creator_friendly (bool): Whether the track is available for Content Creator licenses.
        debut_date (str): ISO8601 timestamp. The first release date of the track.
        is_downloadable (bool): Whether the track can be downloaded with Gold.
        duration (int): The duration of the track in seconds.
        is_explicit (bool): Whether the track is explicit.
        genre (str): Primary genre of the release.
        subgenre (str): The subgenre of the release.
        isrc (str): The International Standard Recording Code of the track.
        uuid (str): The unique ID of the track.
        is_early_access (bool): Whether the track is currently in Gold Early Access.
        lock_status (str): (?).
        is_public (bool): Whether the release's page is public (?).
        release (Release): Release object.
        is_streamable (bool): Whether the track can be streamed on the site/player.
        tags (list[str]): The list of tags of the release.
        title (str): The title of the track.
        number (int): The track number of the track in a release.
        version (str): The version of the track (shown in parentheses).
    """

    artists: list[TrackArtist]
    artists_string: str
    bpm: int
    brand: Brand
    brand_string: str
    is_creator_friendly: bool
    debut_date: str
    is_downloadable: bool
    duration: int
    is_explicit: bool
    genre: str
    subgenre: str
    isrc: str
    uuid: str
    is_early_access: bool
    lock_status: str
    is_public: bool
    release: Release
    is_streamable: bool
    tags: list[str]
    title: str
    number: int
    version: str

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "Artists": "artists",
            "ArtistsTitle": "artists_string",
            "BPM": "bpm",
            "BrandId": "brand",
            "Brand": "brand_string",
            "CreatorFriendly": "is_creator_friendly",
            "DebutDate": "debut_date",
            "Downloadable": "is_downloadable",
            "Duration": "duration",
            "Explicit": "is_explicit",
            "GenrePrimary": "genre",
            "GenreSecondary": "subgenre",
            "ISRC": "isrc",
            "Id": "uuid",
            "InEarlyAccess": "is_early_access",
            "LockStatus": "lock_status",
            "Public": "is_public",
            "Release": "release",
            "Streamable": "is_streamable",
            "Title": "title",
            "TrackNumber": "number",
            "Version": "version",
        }

    @classmethod
    def from_json(cls, data: str) -> "LatestRelease":
        d = json.loads(data, strict=False)
        return LatestRelease.from_dict(d)


@dataclass
class LatestReleasesResponse(JSONSerializable):
    """Class represents Latest Release Response object

    Attributes:
        limit (int): Amount of returned releases.
        offset (int): Amount of skipped releases.
        total (int): (?).
        data (list[LatestRelease]): Returned data from "/catalog/latest-releases" query.
    """

    limit: int
    offset: int
    total: int
    data: list[LatestRelease]

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "Limit": "limit",
            "Offset": "offset",
            "Total": "total",
            "Data": "data",
        }

    @classmethod
    def from_json(cls, data: str) -> "LatestReleasesResponse":
        d = json.loads(data, strict=False)
        return LatestReleasesResponse.from_dict(d)


@dataclass
class ReleasesResponse(JSONSerializable):
    """Class represents Releases Response object

    Attributes:
        limit (int): Amount of returned releases.
        offset (int): Amount of skipped releases.
        total (int): (?).
        data (list[Release]): Returned data from "/releases" query.
    """

    limit: int
    offset: int
    total: int
    data: list[Release]

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "Limit": "limit",
            "Offset": "offset",
            "Total": "total",
            "Data": "data",
        }

    @classmethod
    def from_json(cls, data: str) -> "ReleasesResponse":
        d = json.loads(data, strict=False)
        return ReleasesResponse.from_dict(d)


@dataclass
class RelatedReleasesResponse(JSONSerializable):
    """Class represents Related Releases Response object

    Attributes:
        limit (int): Amount of returned releases.
        offset (int): Amount of skipped releases.
        total (int): The total amount of results for the search.
        data (list[Release]): Returned data from "/releases" query.
    """

    limit: int
    offset: int
    total: int
    data: list[Release]

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "Limit": "limit",
            "Offset": "offset",
            "Total": "total",
            "Data": "data",
        }

    @classmethod
    def from_json(cls, data: str) -> "RelatedReleasesResponse":
        d = json.loads(data, strict=False)
        return RelatedReleasesResponse.from_dict(d)


@dataclass
class Email(JSONSerializable):
    uuid: str | None = None
    email: str | None = None

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "Id": "uuid",
            "Email": "email",
        }

    @classmethod
    def from_json(cls, data: str) -> "Email":
        d = json.loads(data, strict=False)
        return Email.from_dict(d)


@dataclass
class AuthData(JSONSerializable):
    email: Email | None = None
    totp: dict | None = None

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "Email": "email",
            "TOTP": "totp",
        }

    @classmethod
    def from_json(cls, data: str) -> "AuthData":
        d = json.loads(data, strict=False)
        return AuthData.from_dict(d)


@dataclass
class SignInResponse(JSONSerializable):
    is_needs_2fa: bool
    # default_auth_type: str | None = None
    # auth_data: AuthData | None = None

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "Needs2FA": "is_needs_2fa",
            "DefaultAuthType": "default_auth_type",
            "AuthData": "auth_data",
        }

    @classmethod
    def from_json(cls, data: str) -> "SignInResponse":
        d = json.loads(data, strict=False)
        return SignInResponse.from_dict(d)


@dataclass
class StreamableTrackResponse(JSONSerializable):
    signed_url: str

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "SignerURL": "signer_url",
        }

    @classmethod
    def from_json(cls, data: str) -> "StreamableTrackResponse":
        d = json.loads(data, strict=False)
        return StreamableTrackResponse.from_dict(d)


@dataclass
class DownloadableTrackResponse(JSONSerializable):
    signed_url: str
    filename: str

    class Meta(JSONSerializable.Meta):
        json_key_to_field = {
            "SignerURL": "signer_url",
            "Filename": "filename",
        }

    @classmethod
    def from_json(cls, data: str) -> "DownloadableTrackResponse":
        d = json.loads(data, strict=False)
        return DownloadableTrackResponse.from_dict(d)
